In this lecture, we're going to be taking a look at memory stores.
Memory stores are very similar to data stores, but they allow you to have higher throughput and low
latency data storage.
That also allows you to share and store data between multiple servers very quickly.
Memory stores have a much higher API request limit, which is also dependent on how many players are
in your game.
So memory stores are a great and flexible option when you need to constantly update data very quickly,
but also share that data between multiple servers.
If we take a look at the Roblox documentation, it states that the Memory Stores service is a high throughput
and low latency data service that provides fast, in-memory data storage accessible from all servers
in a live session.
Memory stores are suitable for frequent and ephemeral data that can change rapidly and don't need to
be durable because they are faster to access and vanish when reaching the maximum lifetime.
For data that needs to be persistent and static across sessions, you should use data stores.
They give us some examples of what we could use memory stores for.
For example, you could use it for skill based matchmaking.
Save user information such as skill level and a shared queue among servers, and use lobby servers to
run matchmaking periodically.
You can use it for cross server trading and auctioning.
Enable universal trading between different servers on items where users can bid on items with real time
changing prices stored inside a map as key value pairs.
Global leaderboards store and update user rankings on a shared leaderboard inside a map with key value
pairs.
Cache for persistent data sync and copy your persistent data in a data store to a memory store.
Map with key value pairs to function as caches, which can help improve your experiences performance.
So there are basically two structures in memory stores.
You can have a queue, which is basically an array, and you can have a map which acts as a dictionary.
So one is based on index value pairs, while the other is based on key value pairs.
Your sorted maps are going to be for your dictionary based application, and your keys are going to
be for your array based applications.
As an example, with memory stores, a project that I'm working on, I'm using memory stores to store
information about private servers that are being hosted somewhere.
You can think of them as like a session.
And if some kind of player accidentally disconnects from that private server and then rejoins the game,
the server can check to see if they were inside of a previous session, and it will prompt them to rejoin
that session because they disconnected.
If the private server closes, then that session information is removed from the memory store and the
server won't check for that particular player.
Now, since game sessions are going to be short lived and lots can be created quickly, it makes the
most sense to me to store them inside of a memory store.
Now, of course, just like data stores, memory stores also have API request limits and a data structure
size limits.
Of course, these limits are much higher than data stores because you're going to be using these very
quickly.
But let's go ahead and take a look at the limits real quick.
It says for API request limits there's a request unit quota applying to all API calls, which is 1000
plus 100 times the number of concurrent users request units per minute.
Additionally, the rate of requests to any single queue or sorted map is limited to 100,000 request
units per minute.
Most API calls only consume one request unit, with the exceptions of the JIT range, async function
for sorted maps, and the read async function for queues.
These two methods consume units based on the number of returned items with at least one request unit.
For example, if the JIT range async function returned ten items, the total quota counts based on a
ten request units.
If it returns an empty response without items, the quota counts based on a single request unit and
then read.
Async consumes an additional unit every two seconds while reading.
The maximum read time is specified using the wait timeout parameter.
A bunch of technical details, but as long as you set up your memory stores correctly, you shouldn't
have to worry about hitting these different limits.
When it comes to a sorted map or a queue in a memory store, there is a following size and item count
limit, so you can have a maximum number of 1000 keys or indexes, and then you can have a maximum total
size of 100 megabyte per sorted map or queue.
So if you think you're going to exceed this 100 megabyte or this 1000 number limit, then you might
want to split your code or your memory store up into multiple different sorted maps or multiple different
queues.
This article also talks about some best practices when you're using memory stores to avoid hitting the
limits, and a bunch of other information.
This article will be linked in this lecture for you to go ahead and read through it if you'd like to
learn more information.
But enough talking about this.
Let's actually go ahead and go to the Memory Store documentation page so we can take a look at all of
the different API functions.
So right now the memory store service has two working functions.
One is git queue and the other one is git sorted map.
So one of them returns an array or a memory store queue, and the other returns a memory sorted map.
There's this other function here called git hash map.
This is not currently working as the recording of this lecture I tried.
It doesn't do anything.
It actually spits out an error.
So this seems to be an incomplete feature that you cannot use at the moment.
So we're just going to be focusing on these two functions.
So the git queue function returns a memory store queue instance.
And the git sorted map returns a memory store sorted map instance.
Let's go ahead and first take a look at memory store queue.
And it says Memorystore.
Q provides access to a queue within Memorystore.
A queue is a data structure that provides temporary storage for arbitrary items, and as you can see,
it has a few functions.
One is to add a value into the queue.
One is to read a certain number of values from the queue, and then once to remove items from the queue
based on an ID.
So when you read items from the queue, you'll get a count which will be the number of items in the
queue that you want to read.
Let's say it's five items and then this will return um, some different values.
So if we actually head down to the documentation it says it returns, uh, two elements.
The first element is array of item values that were stored in the queue.
And then the second element is a string identifier.
And this is the string identifier that you would use for the remove async function to remove certain
values from your memorystore.
Because, for example, once you read certain values and do something with it, you'll likely want to
discard those values and not use them anymore.
So that's why this function returns a string ID, which you can use in the remove async function to
remove those values from the queue.
Now, something also interesting to note is that when you add items or values into your memorystore
queue, you can also give it an optional priority and it says items with higher priority are retrieved
from the queue before items with a lower priority.
So if there is particular values you want to store inside of this array that you want to receive first
before other items, you can set them to have a higher priority.
You are also able to control the expiration date for a particular value inside of your memory store.
Because memory stores are not permanent storages of values, they will expire.
The default expiration, I believe, is 45 days.
But sometimes you don't need to store values in your memory store for 45 days.
Maybe you only need to store it for like an hour or less, so you can define how long you want the expiration
to be in seconds with this other optional parameter.
Okay, that's enough of me talking.
Let's actually go ahead and see memory stores in action.
Let's go ahead and make a variable for the memory store service.
So we'll do game and get service memory store service.
And then let's go ahead and get a memorystore queue.
I'll create my variable.
I'll just call it queue.
And this is going to be equal to the Memorystore service.
Get queue.
And we want to give a name for this queue.
And this name will be universal across multiple different places in your experience.
So that way all your places can access the same memorystore queue.
I'll just call this my example queue, but you can name it whatever you want.
Just make sure it's a good name for whatever you're going to be using this memorystore for.
And then there's an optional parameter here called invisibility timeout and it says returns a memorystore
queue instance private for that name.
And then this timeout says invisibility timeout in seconds for read operations through this queue instance.
If not provided, defaults to 30s.
If we take a look at the documentation.
As you can see, we have the exact same description for this invisibility timeout.
And it's not exactly clear what this means, but I'll break it down for you.
Basically, when you use different read functions like the read async function for a memorystore queue,
you want to basically be able to hide or make the values that you're reading invisible from the queue.
So that way other servers or other, let's say other functions, don't read these values at the same
time.
Another function is reading these values.
So it hides basically those values from being read when you're currently reading it through an operation.
And the default is 30s.
So yeah, just leave this at 30.
You're not going to have to worry about this that much.
So let's go ahead and leave this at the default value of 30s.
And let's just go ahead and get our queue.
Now what I want to do is I want to get my player service.
So let's actually grab that player's is equal to game get service players.
And then let's go ahead and listen for when a player is added to the game we'll get that player.
And then let's go ahead and listen for when this player chats.
Because what I want to do is for demonstration purposes, I want to store the message that they send
in the chat inside of my memory store.
So the easiest way to do this is we can refer to our queue and we can add an item into it using the
add async function.
The value we want to add is, well, just their string message.
So we can just pass the message here for the expiration date.
We don't need to store this value that long in the data store.
Definitely not 45 days.
So let's just set it to a value of like 100 seconds.
After 100 seconds, this value will automatically be removed from our memory store.
Now the last value here is going to be a optional priority.
And it says items with higher priority are retrieved from the queue before items with a lower priority.
And I basically want to have the ability, when I'm reading values from this queue to get all of the
messages that are longer first.
So the easy way to do that is to set the priority equal to the length of the message, so longer messages
have a higher priority.
Now, since this is an asynchronous function, there is the opportunity or a chance that this function
could error and your code could stop working.
So just in case, let's go ahead and wrap this in a p call so we can do success.
Error is equal to p call.
And then we can just copy this and place it in there.
And if for some reason we were not successful in storing this value inside of our memorystore queue,
we can just warn and spit out that error message.
Now to begin actually reading values from this memorystore queue, we're going to need to have a while
loop that basically just runs forever.
We can have it run every maybe one second or 10s.
Let's do 10s to read information from the Memorystore.
And that's because there isn't any events that you can listen to for when a value is added into a queue,
you're just going to have to actively pull and grab data from your memorystore.
So what we're going to go ahead and do here is we're going to use the function inside of our memorystore
queue.
That is going to be the read async function.
We can give it the number of items that we want to read from the queue.
So let's go ahead and just grab let's say the first five messages from the queue.
Now there's this other optional parameter called all or nothing.
And it says controls the behavior of the method.
In the case the queue has fewer than count items.
If set to false, the method returns all available items.
If set to true, it returns no items.
The default is false.
So for example, let's say there's three items in the queue, but we want to grab five.
If we set it to false, it's just going to return to us those three values.
But if we set it to true then it's going to return nothing because we want five values, but our queue
only has three values inside of it.
We're just going to leave this at the default of false.
And then this last optional parameter is called wait timeout.
And it says the duration in seconds for which the method will wait.
If the required number of items is not immediately available in the queue.
Reads are attempted every two seconds during this period.
This parameter can be set to zero to indicate no wait.
If this parameter is not provided or set to negative one, the method will wait indefinitely.
So let's say we want to grab five items, but our queue has only one item.
We could specify a wait timeout of how long we want to wait before we get those five items to show up,
or if we just leave it at the default, then this function right here is going to wait and yield until
we get those five items.
Since these are both optional parameters, I'm actually just going to leave them both at the default,
and we're just going to grab the first five items inside of our queue.
Now, since this is also an asynchronous function, we need to wrap it inside of a p call so we can
do success.
Uh, result is equal to p call.
Wrap that in a function, put that in there and we want to return the result from our function.
And this result should be a table containing all of our items.
So we could actually rename this to items if we were not successful in grabbing the items.
If there was some kind of error, then we can just spin out the warning for the error message which
will be stored in our items variable.
Otherwise, what we can go ahead and do is we can check if the number of items we received is greater
than zero.
So at least we got something.
So if the length of our table is greater than zero, then let's just go ahead and print out all those
items we got.
We could say this is from our queue and we'll print out these are the items that we got.
And then after we're done messing with these values, for example, maybe we need to do other stuff
with it.
I'm just going to print it out for now.
Once we're done with these items, we should probably remove them from the queue because we don't want
to read them again in the future.
So this is where we need to refer to our queue and use the remove async function.
So let's also wrap that in a p call.
We could do success and then uh get the error message that's going to be equal to p call function.
We refer to our queue and we want to remove these values from our queue.
But remember we need to pass an ID here.
Wait where do we get the ID from.
Well thankfully the read async function, if you remember, returns the id so we can create one more
variable here.
We'll call this our id.
And this is the ID for those particular values that we got.
And then we can pass that ID to our remove async function to remove them from our memorystore queue.
And then of course if we were not successful in doing that, we'll just spit out whatever that error
message is into the console.
Okay.
We should basically have everything coded and set up for our Memorystore queue.
Let's go ahead and test it out by playtesting the game.
Make sure your game is published if you want to test this out.
And then you also have to make sure inside of your game settings that you have enable Studio Access
to API services enabled, or else this won't work.
But I already have all of that enabled, so I'm going to play test my game.
And then I can type out some messages like hello?
I can type out howdy, and I could type out hi.
And then as you can see after 10s we got some values from our queue.
And if we take a look as you see we got hello.
We got howdy.
And we also got hi.
Those are the three messages we got.
And if you notice the messages that are longer are at the beginning of the queue and the smaller message
is at the end of the queue.
And this is true for all sorts of different messages.
So if I say hello there and then I say hello and then I say oops, looks like the queue already happened.
Let me go ahead and take a look at that.
There we go.
Hello.
There is first because it's the longer string and hello is second because it's the shorter string.
And that's because we're using that priority value when we're storing stuff in our queue.
Now, if I were to get rid of this priority and just store stuff in my memorystore queue, then when
we read the items inside of it, it's probably just going to be based on when those different messages
were added into the queue.
So if I send a small message like hi, then hello, and then hi there, we should get a table containing
the messages in that order.
And there we go.
Hi.
Hello and hi there.
So it's in the same order that we sent them out inside of the chat.
Let's go ahead and do that again.
We could do hello.
We could do hi and we could do was up.
Oops.
Looks like the queue already printed.
Okay.
We got.
Hello.
Hi.
Let's try that again.
Let's do.
Um, actually, you know what WhatsApp should print in the next queue?
Let's see.
Okay.
Yeah.
There we go.
There it is.
Because WhatsApp was sent after this queue printed.
We got our first two messages, but our WhatsApp was still inside of the queue, so it wasn't going
to be handled and cleaned up until the next iteration of that loop, which is every 10s.
So that is memory store.
Cuz let's go ahead and take a look at the next data type which is going to be memory store maps.
So let's go ahead and just I'm just going to delete all of this code because I don't need it anymore.
We'll just delete that.
We'll delete that.
And now let's go ahead and make a variable for our sorted map from our memory store.
So I'll just call this sorted.
That's going to be equal to the memory store service.
And we want to go ahead and get a sorted map.
And we got to give it a name I'll just call this my uh example map.
Now of course, our map has some additional functions inside of it.
For example, we can set a value to a particular key.
We can remove a value and a particular key from the sorted map.
We can um, I believe there's a function as well called.
Yep.
We can update a particular key and change its key and its sort key.
And then we can also I believe we can read what is it called read.
No, no no what what is it get I believe it's get async.
Yeah okay.
There we go.
Get async or get sorted.
Dang.
What is it called?
Get okay.
It's called get range.
Yeah that's right get range async.
And you can receive a range of item in the map.
So this is quite similar to memory stores.
But let's go ahead and demonstrate this here.
What I want to do is when the player sends a message I want to store it inside of our map at some kind
of random key.
It doesn't really matter what key it is, but let's go ahead and first make a P call because again these
are async functions.
They can error.
So let's refer to our sorted map.
And let's use the set async function to set a value in a particular key.
Now this key can be whatever you want.
If you need to basically have the same key to be used across multiple different places, you might want
to standardize this.
Maybe you want to use like user IDs of the player.
I'm not really concerned about keys right now, so I'm just going to do like Math.random to generate
a random key.
And the value we want to store here is just going to be the message as well.
We can set a custom expiration date.
I'm also going to do 100 seconds.
And then we are able to give it an optional sort key.
So this is very very similar to the priority value that was in the memorystore queue.
This basically allows us to grab or sort items inside of this um dictionary by using the sort key,
but it's a little bit different.
The smaller the value of the sort key, the higher priority a value will be when we're sorting it.
So basically, if the value is like negative one, that is a higher sort priority than a value of 0
or 1.
So this goes in the other direction.
It's actually negative.
That gets higher priority.
But for the sort key we're just going to do the exact same thing that we did with our queue.
We're going to set it equal to the length of the message.
Now we can go ahead and check to see if this P call was successful and if we got an error message from
it.
So that way if we were not successful, then we can just spit out a warning and say something like or
no, we'll just we'll just spit out the error message.
Now the other function I actually want to demonstrate with the sorted Memorystore map is the update
async function.
So what I'm going to do is I'm going to check if this message the player send is equal to a string of
like um, let's specify something specific here.
We could do exclamation mark update.
This is how I'll demonstrate it.
If it's equal to this message, then we'll do something with the update async function.
Otherwise we'll just save that message inside of our sorted map.
But what we'll do here is that if the player says exclamation mark update, let's go ahead and use the
update async function.
And the key is going to be, um, oh wait, we need to standardize a key here because we're not going
to know what Math.random is.
So let's actually add, uh, another like special message.
So if the message is equal to let's say uh save, then let's go ahead and save a key value pair inside
of our map.
That's going to be using the player's user ID as the key.
So we can do actually let me just copy this.
What we'll do here is we'll use the user ID of this player and the value will store here is um, we
could actually just do the exact same thing here.
We'll just paste that here.
We'll store the message which should be this and give it the same priority.
It doesn't really matter, but we'll put that inside of the memory store.
And of course, if that's not successful, we'll warn and say what the error is in the console.
But now, since we should be able to save this data for this player's user ID, that means we can update
it based on their user ID.
So we'll pass the user ID of the player here.
And then we need to give it a transform function.
So this is very similar to the update async function that you may see inside of uh data stores.
And this function is going to be passed the value that is stored at this key.
And then it's also going to pass the sort key if it exists.
So what we'll do here is I'll just print out what the previous value that was stored in here was.
So we could do value and then also print out the previous sort key.
We'll print that out into the console.
And then afterwards I need to return the new value and the new sort key that we want to replace at this
particular key.
So um, let's just do some random value like I'll save a new string and we'll just call it something
like new value, and then we'll just put in some kind of random sort key like a value of 100.
Doesn't really matter.
Okay.
Now we have all of the saving logic set up for our, uh, memory store map.
Is there anything else I need to add here?
Oh.
Looks like.
We can also specify the expiration as well for this new value and this new sort key inside of the update
async function.
So let's also make sure to specify that we want it to exist for 100 seconds for the update async function
okay.
Now we can go to our while loop.
And what I want to do here is I want to first get my player, which is um, what I could do is I could
do player is equal to players.
I'll get my players in the game and I'll just grab the first player that is in the game.
Uh, if there is no player, then we'll just continue looping until we get a player.
Otherwise.
This is where we can do the logic for our memory store.
So let's refer to our sorted memory store map and let's use the get range async function.
We have to give it an enum of sort direction, the count, or how many key value pairs we want to look
at.
And then there's some other optional parameters here.
But let's go ahead and do the enum dot sort order of or oops sort direction of.
Let's do it in ascending order.
Let's go ahead and grab the first five elements, just like we did for the queue.
And then these other optional parameters can specify like a limit, a lower bound, and an upper bound
limit, and it says for the exclusive lower bound it says exclusive for the return keys.
This is provided as a table where one or both of key and sort key can be specified.
And this basically allows us to stop getting um, values from our sorted map based on the key and the
string.
So if there's any keys that would be considered less than our exclusive lower bound, we're going to
not get them and ignore them.
And that's going to be the same thing with the sort key.
If there's any sort keys that are considered lower than the lower bound we specify, it's not going
to grab those either.
And we can do that for the upper bound as well.
So what I'm going to do is I'm going to make a variable.
I'm going to call this my lower bound.
And for now we're going to initialize it with no value.
But we are going to pass it to the function.
This is going to be okay.
We can pass nil here because these uh parameters they are optional.
So it's okay if we pass nil here it's not going to error.
And then after we get that range of items, we can do whatever we want with them.
And then we can update this variable here to a table that specifies the lower bound of the key and the
sort key.
So that way we don't accidentally grab the same values again.
So again let's go ahead and make a p call here.
We'll do success.
And then we'll get an array of items returned from our get range async function.
We'll make sure to return that.
This will return a table of the items, just like with our memory queue.
If we were not successful in getting items, we'll just spit out an error message.
Or oops that should be stored in the items variable.
Otherwise, if the number of items we get is greater than zero, then we can go ahead and basically
just print out what is stored inside of the map.
So I'll just print what items we've got in there.
And then as this while loop continues looping, we're eventually going to reach the end of this sorted
map because we're going in ascending order.
So what we can do is we can check if the number of items is less than five, which is less than the
value that we specified here.
When we're getting the range of items, if it's less than five, this means we've basically reached
the end of our sorted map.
So we could just continue to the next iteration of this loop.
Otherwise, if we didn't reach the end of our sorted map, we need to go ahead and shift the lower bound
so that way we can get the next five items.
Because if we don't specify a lower bound, it's just going to keep grabbing the same five items over
and over and over again.
So we're going to set our lower bound equal to a table.
And if you remember in the parameter section it has to be a table that has uh two keys.
One is called key and the other one is called sort key.
So we can do lower bound dot key.
And we need to set this equal to the last element we got inside of our items table.
So we could do items.
We'll get whatever item at the last um element inside of this table.
So that's just going to be the length of the items.
And actually let's go ahead and head to the documentation real quick.
And inside of the documentation, if we look at the get range async function and we look at their code
example when they're looping through all of the items that is returned by that function, when the item
is a table that stores the key, the sort key, as well as the value, um, for that particular item
as well.
So there's three keys inside of their key sort key and value.
And that's what we need to refer to inside of our code.
So when we're grabbing this last item inside of our items, uh, table, we can go ahead and refer to
its key and set that as the lower bound.
And then we can do the exact same thing for the sort key as well.
We can just copy this, but set this equal to the last sort key inside of our items table.
So now we should be able to properly loop through all of the different messages that are going to be
stored inside of our map right here.
Now you might be wondering wait a minute, there's no yield statement in here.
What are you doing?
And that's because technically this function does yield, so it won't crash your game.
But considering the fact that who knows how long it's going to be before we grab our first player,
we should actually probably put a yield statement in here.
So we'll do the same thing of 10s.
So that way we don't crash the game, and then we should have all the logic set up for our sort map.
So actually let's go ahead and play test the game and try it out.
I'll send some messages in the chat, like.
Hello?
Howdy.
Uh, hi.
What's up?
And let's go ahead and see what prints.
Okay, we get our map, and as you can see, it stores the different items, which are tables.
And if we take a look at each one of those items, they have a key a sort key and the value.
And as you can see we get hi hello.
Howdy.
And was up.
And since we specified that we wanted to grab values in an ascending order, if you can tell we got
it in in ascending order because the smallest one was high, then we're going in ascending order as
we continue through the array.
If we wanted to flip this around and get the longer messages first, then we would have to swap it from
ascending to descending order.
And if you notice down here, it's continuing to print those same messages out because they're still
stored within our memory store.
They're not going to be automatically removed until 100 seconds later.
And also we did not, uh, remove them from the queue after we read them.
So they're still stored inside of the memory store.
Let's go ahead and wait 100 seconds and see if these values get automatically removed.
So I'll just sit here and wait.
All right.
And as you can see after this printed a total of ten times.
And since we're waiting 10s between each loop, that's 100 seconds.
It is no longer printing out those values inside of the console, because they were automatically removed
and cleaned up out of our memory store.
They are gone forever.
Let's go ahead and test out some of the other features inside of the memory store, such as, for example,
uh, write an exclamation mark.
Save!
We should save this message inside of our memory store, and it should be associated with the user ID
of our player.
So if we go ahead and take a look and we wait here, hopefully it prints something.
There we go.
There is my user ID right there.
There is the sort key which is the length of my string.
And as you can see it's exclamation mark.
Save.
Very cool.
Now let's go ahead and use that other message which was update to change that value.
There we go.
The previous value was save and the previous sort key was five.
And now we should have a new value stored in place at the same key.
There you go.
The exact same key which is our user ID, but now it has a sort key of 100 and it's storing our new
value.
And of course this value also has an expiration lifetime of 100 seconds.
So after 100 seconds this will completely disappear from our memory store.
Okay.
So this was likely quite a long lecture packed with a boatload of information.
If you don't understand everything right away, that's absolutely okay.
You can always come back to this lecture, go through different parts and rewatch the areas that you
didn't understand.
And I hope this lecture has given you a better understanding of data stores and how to use the API to
interact with them.
Before I let you go, one last thing I would like to show you is the Memory Store viewer.
In the Creator Hub.
You can actually view the status and how close you're getting to the limitations of memory stores for
each of your games.
So let's go ahead and take a look at that in the Creator Hub.
All right.
Here in the Creator Hub we're going to go to our game that is using memory stores.
In this case it is my memory store uh service place.
We can select our game and then we can go to the monitoring tab.
And there is a section to monitor the status of your memory stores.
So if we take a look at this and we look at the West one, our um, actually considering the fact that
I think we were doing it through studio, it's not going to display any information in here, which
is actually a little bit unfortunate.
So let me actually swap to a different account that has used memory stores, so I can show you what
these charts look like.
Okay, so here's an example of a place that I'm currently working on where I was messing around with
data stores.
And this is what your charts should look like.
It'll show you what your memory quota is based on the number of players in your game.
So my memory quota and bytes is like around 60 something thousand.
And down here, this blue bar will show you the memory usage that you're currently using in your data
store.
As you can see, I barely ever even reached the memory quota, so there's no issues there.
If you see that your memory usage is getting very close to your memory quota, you might want to reevaluate
how you're using your memory stores because it's going to cause you issues when you hit that quota.
But we can go ahead and take a look at some of the other different, uh, analytics here.
For example, the API request units.
So this will show you how many request units I've used and my quota for my, uh, request unit.
So I get like 2000.
And as you can see, I'm like barely using any request units at all.
So there's not much data to show here.
And then if we go down here, we can see request count by API.
So we can see like the different functions that used some different amounts.
Like I used one request here, I used I think one right here and one right here.
And then we can also see something called request count by status.
It says views the breakdown of API requests uh, by response status to identify top issues.
So there's requests per minute.
And then I believe, oh, these are like different statuses of the different requests.
So success, success, no items found.
And then there's some other extra statistics in here.
But this is basically how you can monitor your memory stores to see if there are any issues going on
in your game.
Alrighty, I hope you enjoyed this long, long lecture and I will see you in the next one.